Skip to content
标签
交互
字数
3948 字
阅读时间
18 分钟

一、概述

webservice 即web服务,它是一种跨编程语言和跨操作系统平台的远程调用技术。

webService三要素:soap、wsdl、uddi

1.1 开发规范

JAVA 中共有三种WebService 规范,分别是JAX-WS(JAX-RPC)、JAXM&SAAJ、JAX-RS。

  • JAX-WS AX-WS(Java API For XML-WebService),JDK1.6 自带的版本为JAX-WS2.1,其底层支持为JAXB。JAX-WS(JSR 224)规范的API 位于javax.xml.ws.*包,其中大部分都是注解,提供API 操作Web 服务(通常在客户端使用的较多,由于客户端可以借助SDK 生成,因此这个包中的API 我们较少会直接使用)。

  • JAXM&SAAJ JAXM(JAVA API For XML Message)主要定义了包含了发送和接收消息所需的API,相当于Web 服务的服务器端,其API 位于javax.messaging.*包,它是JAVA EE 的可选包,因此你需要单独下载。 SAAJ(SOAP With Attachment API For Java,JSR 67)是与JAXM 搭配使用的API,为构建SOAP 包和解析SOAP 包提供了重要的支持,支持附件传输,它在服务器端、客户端都需要使用。这里还要提到的是SAAJ 规范,其API 位于javax.xml.soap.*包。 JAXM&SAAJ 与JAX-WS 都是基于SOAP 的Web 服务,相比之下JAXM&SAAJ暴漏了SOAP更多的底层细节,编码比较麻烦,而JAX-WS 更加抽象,隐藏了更多的细节,更加面向对象,实现起来你基本上不需要关心SOAP 的任何细节。那么如果你想控制SOAP 消息的更多细节,可以使用JAXM&SAAJ,目前版本为1.3。

  • JAX-RS JAX-RS 是JAVA 针对REST(RepresentationState Transfer)风格制定的一套Web 服务规范,由于推出的较晚,该规范(JSR 311,目前JAX-RS 的版本为1.0)并未随JDK1.6 一起发行,你需要到JCP 上单独下载JAX-RS 规范的接口,其API 位于javax.ws.rs.*包。 这里的JAX-WS 和JAX-RS 规范我们采用Apache CXF 作为实现,CXF 是Objectweb Celtix和Codehaus XFire 合并而成。CXF 的核心是org.apache.cxf.Bus(总线),类似于Spring 的ApplicationContext,Bus 由BusFactory 创建,默认是SpringBusFactory 类,可见默认CXF是依赖于Spring 的,Bus 都有一个ID,默认的BUS 的ID 是cxf。你要注意的是Apache CXF 2.2 的发行包中的jar 你如果直接全部放到lib 目录,那么你必须使用JDK1.6,否则会报JAX-WS 版本不一致的问题。对于JAXM&SAAJ 规范我们采用JDK 中自带的默认实现。

1.2 SOAP 协议

(1)   SOAP即简单对象访问协议(Simple Object Access Protocol),它是用于交换XML(标准通用标记语言下的一个子集)编码信息的轻量级协议。它有三个主要方面:XML-envelope为描述信息内容和如何处理内容定义了框架,将程序对象编码成为XML对象的规则,执行远程过程调用(RPC)的约定。SOAP可以运行在任何其他传输协议上。 (2)   SOAP作为一个基于XML语言的协议用于有网上传输数据。 (3)   SOAP = 在HTTP的基础上+XML数据。 (4)   SOAP是基于HTTP的。 (5)   SOAP的组成如下 a)  Envelope – 必须的部分。以XML的根元素出现。 b)  Headers – 可选的。 c)  Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。

1.3 WSDL 说明书

Web Service描述语言WSDL(SebService Definition Language)就是用机器能阅读的方式提供的一个正式描述文档而基于XML(标准通用标记语言下的一个子集)的语言,用于描述Web Service及其函数、参数和返回值。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的。

wsdl说明书, 1) 通过wsdl说明书,就可以描述webservice服务端对外发布的服务; 2) wsdl说明书是一个基于xml文件,通过xml语言描述整个服务; 3) 在wsdl说明中,描述了: ​ 对外发布的服务名称(类) ​ 接口方法名称(方法) ​ 接口参数(方法参数) ​ 服务返回的数据类型(方法返回值)

1.4 UDDI

Web 服务提供商又如何将自己开发的 Web 服务公布到因特网上,这就需要使用到 UDDI 了,UDDI的话,是一个跨产业,跨平台的开放性架构,可以帮助 Web 服务提供商在互联网上发布 Web 服务的信息。

UDDI 是一种目录服务,企业可以通过 UDDI 来注册和搜索 Web 服务。简单来时候话,UDDI 就是一个目录,只不过在这个目录中存放的是一些关于 Web 服务的信息而已。并且 UDDI 通过SOAP 进行通讯,构建于 . Net 之上。

UDDI 即 Universal Description,Discovery andIntegration,也就是通用的描述,发现以及整合。

UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为WebService提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。

1.5 优缺点及应用场景

  • 优点: a)  异构平台的互通性 理论上, Web Service 最大的优势是提供了异构平台的无缝街接技术手段。由于不同的用户使用不同的硬件平台,不同的操作平台,不同的操作系统,不同的软件,不同的协议通信,这就产生了互相通信的需求。 Web Service 使任何两个应用程序,只要能读写XML,那么就能互相通信。

    b)   更广泛的软件复用(例如手机淘宝可以复用已有淘宝的业务逻辑.) 软件的复用技术通过组合已有模块来搭建应用程序,能大幅度提高软件的生产效率和质量。用户只要获得了描述 Web Service 的 WSDL 文件,就可以方便地生成客户端代理,并通过代理访问 Web Service 。

    c)   成本低、可读性强、应用范围广 Web Service 可用基于 XML 的 SOAP 来表示数据和调用请求。并且通过 HTTP 协议传输 XML 格式的数据

    d)   迅捷的软件发行方式 (每个web Service称为一个生产者.不同的生产者可以相互协同合作完成整个应用)Web Service 将彻底地改变软件的发行方式。

    软件供应商可以把软件分解成若Web Service 模块构成的系统,直接在 Web 上发布。

    客户端与服务端可能是用不同的语言开发的,但是,通过webservice提供服务接口,客户端与服务端之前可以传递对象。

  • 缺点: 由于soap是基于xml传输,本身使用xml传输会传输一些无关内容从而影响效率,随着soap协议的完善,soap协议增加了许多内容,这样就导致了使用soap去完成简单的数据传输而携带的信息更多效率再受影响; Web Service作为web跨平台访问的标准技术,很多公司都限定要求使用Web Service,但如果是简单的接口可以直接使用http传输自定义数据格式,开发更快捷。

  • 应用场景 Web Service 可以适用于应用程序集成、软件重用、跨防火墙通信等需求。不同的业务要求不同。具体如下:

    (1)   跨防火墙通信

    (2)   应用系统集成 (3)   软件和数据重用

    ​ 简单来说,如果一个功能,需要被多个系统使用可以使用webservice开发一个服务端接口,供不同的客户端应用。主要应用在企业内部系统之间的接口调用、面向公网的webservice服务。

二、实现WebService

2.1 ApacheCXF 实现(Jax-ws)

服务端

  • 添加依赖

    xml
    <!-- 要进行jaxws 服务开发 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxws</artifactId>
        <version>3.0.1</version>
    </dependency>
    
    <!-- 内置jetty web服务器 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http-jetty</artifactId>
        <version>3.0.1</version>
    </dependency>
  • 提供服务

    java
    import javax.jws.WebService;
    
    /**
     * 对外发布服务的接口
     */
    @WebService
    public interface HelloService {
        /**
         * 对外发布服务的接口的方法
         */
        public String sayHello(String name);
    }
    
    
    
    import com.itheima.service.HelloService;
    
    public class HelloServiceImpl implements HelloService {
        @Override
        public String sayHello(String name) {
            return name + ",Welcome to Itheima!";
        }
    }
  • 发布服务

    java
    import com.itheima.service.impl.HelloServiceImpl;
    import org.apache.cxf.interceptor.LoggingInInterceptor;
    import org.apache.cxf.interceptor.LoggingOutInterceptor;
    import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
    
    public class Server {
        public static void main(String[] args) {
            //  发布服务的工厂
            JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
    
            //  设置服务地址
            factory.setAddress("http://localhost:8000/ws/hello");
    
            //  设置服务类
            factory.setServiceBean(new HelloServiceImpl());
    
            //  添加日志输入、输出拦截器,观察soap请求、soap响应内容
            factory.getInInterceptors().add(new LoggingInInterceptor());
            factory.getOutInterceptors().add(new LoggingOutInterceptor());
    
            //  发布服务
            factory.create();
    
            System.out.println("发布服务成功,端口8000.....");
    
        }
    }
  • 访问说明书

    http://localhost:8000/ws/hello?wsdl

客户端

  • 客户端调用
    java
    // 定义接口
    import javax.jws.WebService;
    
    /**
     * 对外发布服务的接口
     */
    @WebService
    public interface HelloService {
        /**
         * 对外发布服务的接口的方法
         */
        public String sayHello(String name);
    }
    
    // 调用服务端
    
    import com.itheima.service.HelloService;
    import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
    
    public class Client {
        public static void main(String[] args) {
            //  服务接口访问地址:http://localhost:8000/ws/hello
    
            // 创建cxf代理工厂
            JaxWsProxyFactoryBean factory = new  JaxWsProxyFactoryBean();
    
            //  设置远程访问服务端地址
            factory.setAddress("http://localhost:8000/ws/hello");
    
            //  设置接口类型
            factory.setServiceClass(HelloService.class);
    
            //  对接口生成代理对象
            HelloService helloService = factory.create(HelloService.class);
    
            //  代理对象对象  class com.sun.proxy.$Proxy34       [Java代理: 1. 静态代理;  2.动态代理(jdk接口代理、cglib子类代理)]    $CGLIB123
            System.out.println(helloService.getClass());
    
            // 远程访问服务端方法
            String content = helloService.sayHello("Jet");
            System.out.println(content);
        }
    }

2.2 Spring 整合 ApacheCXF(Jax-ws)

服务端

  • 依赖

    xml
    <!-- CXF WS开发  -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxws</artifactId>
        <version>3.0.1</version>
    </dependency>
  • web.xml添加配置

    xml
    <!--1. cxfsevlet配置-->
    <servlet>
        <servlet-name>cxfservlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    </servlet>
  • 服务接口及服务实现(同2.1)

  • 配置与spring整合

    xml
    <!--
          Spring整合cxf发布服务,关键点:
           1. 服务地址
           2. 服务类
           服务完整访问地址:
           http://localhost:8080/ws/hello
    -->
    <jaxws:server address="/hello">
        <jaxws:serviceBean>
            <bean class="com.itheima.service.impl.HelloServiceImpl"></bean>
        </jaxws:serviceBean>
    </jaxws:server>
  • 访问wsdl说明书

    http://localhost:8080/ws/hello?wsdl

客户端

  • 添加服务接口(同2.1)

  • 配置与spring整合

    xml
    <!--
            Spring整合cxf客户端配置:
            1. 服务地址     http://localhost:8080/ws/hello
            2. 服务接口类型
    -->
    <jaxws:client
           id="helloService"
           serviceClass="com.itheima.service.HelloService"
           address="http://localhost:8080/ws/hello"></jaxws:client>
  • 测试调用

    java
    import com.itheima.service.HelloService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import javax.annotation.Resource;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class Client {
    
        // 注入对象
        @Resource
        private HelloService helloService;
    
        @Test
        public void remote(){
            // 查看接口的代理对象类型
            // class com.sun.proxy.$Proxy45
            System.out.println(helloService.getClass());
    
            // 远程访问服务端方法
            System.out.println(helloService.sayHello("Jerry"));
        }
    }

2.3 ApacheCXF 实现(Jax-rs)

基于restful风格的webservice,请求使用的是http协议,可以传递xml/json数据

服务端

  • 依赖

    xml
    <!-- jaxrs 支持包 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxrs</artifactId>
        <version>3.0.1</version>
    </dependency>
    
    <!-- 客户端调用时候使用的包(WebClient工具类调用服务端) -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-rs-client</artifactId>
        <version>3.0.1</version>
    </dependency>
    
    <!-- 基于restful风格的webservice,客户端与服务端之间可以传递json,这个就是json支持相关包 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-rs-extension-providers</artifactId>
        <version>3.0.1</version>
    </dependency>
  • 实体类及服务提供

    java
    /**
     * @XmlRootElement 指定根元素,作用:客户端与服务端传递对象数据时候,序列化为xml或json的根元素的名称
     * 客户端与服务端传递XML:
     * 	  <Car1></Car>  这里的根元素Car是由name="Car"指定的
     * 
     * 客户端与服务端传递JSON:
     *    {"Car": {"id":100,"name":"","price":100}}
     * 
     */
    @XmlRootElement(name = "Car")
    public class Car {
    	private Integer id;
    	private String carName;
    	private Double price;   省略get、set
    }
    @XmlRootElement(name = "User")
    public class User {
    	private Integer id;
    	private String username;
    	private String city;
    	private List<Car> cars = new ArrayList<Car>();  省略get、set
    }
    
    @Path("/userService")  // 路径;访问当前服务接口时候的路径。
    @Produces("*/*")
    public interface IUserService {
    
    	@POST
    	@Path("/user")	  // 路径; 访问当前服务接口的方法路径
    	// @Consumes 服务端支持的请求的数据格式(xml、json)
    	@Consumes({ "application/xml", "application/json" })
    	public void saveUser(User user);
    
    	@PUT
    	@Path("/user")
    	@Consumes({ "application/xml", "application/json" })
    	public void updateUser(User user);
    
    	@GET
    	@Path("/user")
    	// @Produces 服务端支持的响应的数据格式
    	@Produces({ "application/xml", "application/json" })
    	public List<User> findAllUsers();
    
    	@GET
    	@Path("/user/{id}")
    	@Consumes("application/xml")
    	@Produces({ "application/xml", "application/json" })
    	public User finUserById(@PathParam("id") Integer id);
    
    	@DELETE
    	@Path("/user/{id}")
    	@Consumes({"application/xml", "application/json"})
    	public void deleteUser(@PathParam("id") Integer id);
    }
    
    
    public class UserServiceImpl implements IUserService {
    
    	public void saveUser(User user) {
    		System.out.println("save user:" + user);
    	}
    
    	public void updateUser(User user) {
    		System.out.println("update user:" + user);
    	}
    
    	public List<User> findAllUsers() {
    		List<User> users = new ArrayList<User>();
    
    		// 汽车
    		List<Car> carList1 = new ArrayList<Car>();
    		Car car1 = new Car(101,"保时捷",1000000d);
    		Car car2 = new Car(102,"林肯",400000d);
    		carList1.add(car1);
    		carList1.add(car2);
    
    		// 用户
    		User user1 = new User(1,"小明","广州",carList1);
    		User user2 = new User(2,"小丽","深圳",carList1);
    
    		// 用户集合
    		users.add(user1);
    		users.add(user2);
    
    		return users;
    	}
    
    	public User finUserById(Integer id) {
    		if (id == 1) {
    			return new User(1,"小明","广州",null);
    		}
    		return null;
    	}
    
    	public void deleteUser(Integer id) {
    		System.out.println("delete user id :" + id);
    	}
    
    }
  • 注解

    java
    @XmlRootElement 指定根元素,作用:客户端与服务端传递对象数据时候,序列化为xml或json的根元素的名称
    
    @Path("/userService")  路径;访问当前服务接口时候的路径、接口方法的路径。
    
    @POST   insert操作
    
    @PUT    update操作
    
    @GET    select 查询操作
    
    @DELETE   delete删除操作
    
    @Consumes 服务端支持的请求的数据格式(xml、json)
    
    @Produces 服务端支持的响应的数据格式
  • 发布服务

    java
    import com.itheima.service.UserServiceImpl;
    import org.apache.cxf.interceptor.LoggingInInterceptor;
    import org.apache.cxf.interceptor.LoggingOutInterceptor;
    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
    
    public class Server {
    
        /**
         * 发布restful风格的webservice的服务
         */
        public static void main(String[] args) {
            //1.创建服务工厂
            JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
    
            //2.设置服务地址、
            factory.setAddress("http://localhost:8001/rs");
    
            //3.实例化服务类、
            factory.setServiceBean(new UserServiceImpl());
    
            // 添加日志拦截器
            factory.getInInterceptors().add(new LoggingInInterceptor());
            factory.getOutInterceptors().add(new LoggingOutInterceptor());
    
            //4.创建服务
            factory.create();
    
            System.out.println("发布服务成功..8001");
        }
    
    }

客户端

  • 依赖(同服务端)

  • 服务访问

    java
    import com.itheima.domain.User;
    import org.apache.cxf.interceptor.LoggingInInterceptor;
    import org.apache.cxf.interceptor.LoggingOutInterceptor;
    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
    import org.apache.cxf.jaxrs.client.WebClient;
    import org.junit.Test;
    
    import javax.ws.rs.core.MediaType;
    import java.util.Collection;
    
    public class Client {
    
        @Test
        public void save() throws Exception {
            // 基于restful风格的webservice开发的客户端调用,直接通过一个类:WebClient类完成
            WebClient
                    .create("http://localhost:8001/rs/userService/user")  // 地址
                    .type(MediaType.APPLICATION_JSON)                     // 请求数据格式是json
                    .post(new User(100,"Kobe","gz",null));                  // 发送请求的类型
        }
    
        @Test
        public void update() throws Exception {
            WebClient
                    .create("http://localhost:8001/ws/userService/user")  // 地址
                    .type(MediaType.APPLICATION_JSON)                     // 请求数据格式是json
                    .put(new User(100,"Kobe","gz",null));                   // 发送请求的类型
        }
    
        @Test
        public void delete() throws Exception {
    
        }
    
        @Test
        public void findOne() throws Exception {
            User user =
                    WebClient
                            .create("http://localhost:8001/ws/userService/user/1")  // 地址
                            .accept(MediaType.APPLICATION_JSON)  // 响应的数据格式
                            .get(User.class);
            System.out.println(user);
        }
    
        @Test
        public void findAll() throws Exception {
            Collection<? extends User> collection =
                    WebClient
                            .create("http://localhost:8001/ws/userService/user")
                            .accept(MediaType.APPLICATION_JSON)
                            .getCollection(User.class);
            System.out.println(collection);
        }
    
    
    }

2.4 Spring 整合 ApacheCXF(Jax-rs)

服务端

  • 依赖

    xml
    <!-- cxf 进行rs开发 必须导入 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxrs</artifactId>
        <version>3.0.1</version>
    </dependency>
    <!-- 客户端 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-rs-client</artifactId>
        <version>3.0.1</version>
    </dependency>
    
    <!-- 扩展json提供者 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-rs-extension-providers</artifactId>
        <version>3.0.1</version>
    </dependency>
    
    <!-- 转换json工具包,被extension providers 依赖 -->
    <dependency>
        <groupId>org.codehaus.jettison</groupId>
        <artifactId>jettison</artifactId>
        <version>1.3.7</version>
    </dependency>
  • web.xml

    xml
        <servlet>
            <servlet-name>cxfservlet</servlet-name>
            <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>cxfservlet</servlet-name>
            <url-pattern>/ws/*</url-pattern>
        </servlet-mapping>
  • 服务提供者(同2.3)

  • 配置整合

    xml
    <!--
            Spring整合ApacheCXF,发布jaxws服务:
            1. 服务地址
            2. 服务bean
    
            完整服务地址:
            http://localhost:8080/ws/userService
         -->
        <jaxrs:server address="/userService">
            <jaxrs:serviceBeans>
                <bean class="com.itheima.service.UserServiceImpl"></bean>
            </jaxrs:serviceBeans>
            <jaxrs:inInterceptors>
                <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
            </jaxrs:inInterceptors>
            <jaxrs:outInterceptors>
                <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
            </jaxrs:outInterceptors>
        </jaxrs:server>

客户端

  • 依赖(同服务端)

  • 实体类(同服务端)

  • 调用

    java
    import com.itheima.domain.User;
    import org.apache.cxf.jaxrs.client.WebClient;
    
    public class Client {
    
        public static void main(String[] args) {
            // 测试1: 保存
            WebClient
                    .create("http://localhost:8080/ws/userService/userService/user")
                    .post(new User());
    
            // 测试2:删除(传入id)
            WebClient
                    .create("http://localhost:8080/ws/userService/userService/user/100")
                    .delete();
        }
    }